package me.mononin.factory.data.helper;

import com.raizlabs.android.dbflow.config.DatabaseDefinition;
import com.raizlabs.android.dbflow.config.FlowManager;
import com.raizlabs.android.dbflow.sql.language.SQLite;
import com.raizlabs.android.dbflow.structure.BaseModel;
import com.raizlabs.android.dbflow.structure.ModelAdapter;
import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper;
import com.raizlabs.android.dbflow.structure.database.transaction.ITransaction;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import me.mononin.factory.model.db.AppDataBase;
import me.mononin.factory.model.db.Group;
import me.mononin.factory.model.db.GroupMember;
import me.mononin.factory.model.db.Group_Table;
import me.mononin.factory.model.db.Message;
import me.mononin.factory.model.db.Session;

public class DbHelper {

    public static final DbHelper instance;

    static {
        instance = new DbHelper();
    }

    private DbHelper() {
    }
    //实现观察者模式
    /**
     * 观察者的集合
     * Class<?>：观察者的表
     * Set<ChangedListener>：每一个表对应的所有观察者
     */
    private final Map<Class<?>,Set<ChangedListener>> changedListeners = new HashMap<>();

    /**
     * 获取某一个表的所有监听器
     * @param modelClass 传入的表参数
     * @param <Model> 表的泛型
     * @return 返回所有的对应表监听器
     */
    private <Model extends BaseModel> Set<ChangedListener> getListeners(Class<Model> modelClass) {
        if (changedListeners.containsKey(modelClass)){
            return changedListeners.get(modelClass);
        }
        return null;
    }

    /**
     * 为某一个表添加一个观察者
     * @param tClass 对应某个表
     * @param listener 观察者监听器
     * @param <Model> 泛型
     */
    public static <Model extends BaseModel> void addChangedListener(final Class<Model> tClass,
                                                                  ChangedListener<Model> listener){
        //获取当前表的所有观察者
        Set<ChangedListener> changedListeners = instance.getListeners(tClass);
        //如果当前表不存在观察者
        if (changedListeners==null){
            //为当前表初始化一个观察者容器
            changedListeners = new HashSet<>();
            //添加到观察者容器中
            instance.changedListeners.put(tClass,changedListeners);
        }
        //观察者容器添加观察者
        changedListeners.add(listener);
    }

    /**
     * 为某一个表删除一个观察者
     * @param tClass 对应某个表
     * @param listener 观察者监听器
     * @param <Model> 泛型
     */
    public static <Model extends BaseModel> void removeChangedListener(final Class<Model> tClass,
                                                                    ChangedListener<Model> listener){
        //获取当前表的所有观察者
        Set<ChangedListener> changedListeners = instance.getListeners(tClass);
        //如果当前表不存在观察者
        if (changedListeners==null){
            //容器为空，代表没有观察期直接返回
            return;
        }
        //观察者容器删除观察者
        changedListeners.remove(listener);
    }

    /**
     * 保存数据到本地
     * @param tClass 传递一个class信息
     * @param models 要保存的数据
     * @param <Model> 数据泛型 限制：必须继承BaseModel
     */
    public static<Model extends BaseModel> void save(final Class<Model> tClass, final Model... models){
        //边界判定
        if (models==null||models.length==0){
            return;
        }

        //获取数据库管理者
        DatabaseDefinition definition = FlowManager.getDatabase(AppDataBase.class);
        //开启一个数据库事物
        definition.beginTransactionAsync(new ITransaction() {
            @Override
            public void execute(DatabaseWrapper databaseWrapper) {
                //适配器
                ModelAdapter<Model> adapter =  FlowManager.getModelAdapter(tClass);
                //保存数据
                adapter.saveAll(Arrays.asList(models));
                // 唤起通知
                instance.notifySave(tClass, models);
            }
        }).build().execute();
    }

    /**
     * 删除本地数据
     * @param tClass 传递一个class信息
     * @param models 要保存的数据
     * @param <Model> 数据泛型 限制：必须继承BaseModel
     */
    public static<Model extends BaseModel> void delete(final Class<Model> tClass, final Model... models){
        //边界判定
        if (models==null||models.length==0){
            return;
        }

        //获取数据库管理者
        DatabaseDefinition definition = FlowManager.getDatabase(AppDataBase.class);
        //开启一个数据库事物
        definition.beginTransactionAsync(new ITransaction() {
            @Override
            public void execute(DatabaseWrapper databaseWrapper) {
                //适配器
                ModelAdapter<Model> adapter =  FlowManager.getModelAdapter(tClass);
                //保存数据
                adapter.deleteAll(Arrays.asList(models));
                //唤起通知
                instance.notifyDelete(tClass,models);
            }
        }).build().execute();
    }

    /**
     * 保存通知调用
     * @param tClass 传递一个class信息
     * @param models 要保存的数据
     * @param <Model> 数据泛型 限制：必须继承BaseModel
     */
    private final  <Model extends BaseModel> void notifySave(final Class<Model> tClass, final Model... models){
        //获取监听器
        final Set<ChangedListener> listeners = getListeners(tClass);
        if (listeners!=null&&listeners.size()>0){
            //通用的通知
            for (ChangedListener<Model> listener : listeners) {
                //通知更新
                listener.onDataSave(models);
            }
        }

        if (GroupMember.class.equals(tClass)){
            //群成员变更时，需要通知对应群信息更新
            updateGroup((GroupMember[]) models);
        }else if (Message.class.equals(tClass)){
            //消息信息变化时，需要通知会话列表更新
            updateSession((Message[]) models);
        }
    }

    /**
     * 删除通知调用
     * @param tClass 传递一个class信息
     * @param models 要保存的数据
     * @param <Model> 数据泛型 限制：必须继承BaseModel
     */
    private final  <Model extends BaseModel> void notifyDelete(final Class<Model> tClass, final Model... models){
        //获取监听器
        final Set<ChangedListener> listeners = getListeners(tClass);
        if (listeners!=null&&listeners.size()>0){
            //通用的通知
            for (ChangedListener<Model> listener : listeners) {
                //通知更新
                listener.onDataDelete(models);
            }
        }

        if (GroupMember.class.equals(tClass)){
            //群成员变更时，需要通知对应群信息更新
            updateGroup((GroupMember[]) models);
        }else if (Message.class.equals(tClass)){
            //消息信息变化时，需要通知会话列表更新
            updateSession((Message[]) models);
        }
    }

    private void updateGroup(GroupMember... members) {
        final Set<String> groupIds = new HashSet<>();
        for (GroupMember member : members) {
            //添加群组Id
            groupIds.add(member.getGroup().getId());
        }

        //获取数据库管理者
        DatabaseDefinition definition = FlowManager.getDatabase(AppDataBase.class);
        //开启一个数据库事物
        definition.beginTransactionAsync(new ITransaction() {
            @Override
            public void execute(DatabaseWrapper databaseWrapper) {
                //列出所有需要通知的群
                List<Group> groups = SQLite.select()
                        .from(Group.class)
                        .where(Group_Table.id.in(groupIds))
                        .queryList();

                //调用直接进行一次通知
                instance.notifySave(Group.class,groups.toArray(new Group[0]));

            }
        }).build().execute();
    }

    private void updateSession(Message... messages){
        //标志Session的唯一标识
        final Set<Session.Identify> identifies = new HashSet<>();
        //遍历
        for (Message message : messages) {
            //创建唯一标识
            Session.Identify identify = Session.createSessionIdentify(message);
            //添加身份
            identifies.add(identify);
        }

        //获取数据库管理者
        DatabaseDefinition definition = FlowManager.getDatabase(AppDataBase.class);
        //开启一个数据库事物
        definition.beginTransactionAsync(new ITransaction() {
            @Override
            public void execute(DatabaseWrapper databaseWrapper) {
                //适配器
                ModelAdapter<Session> adapter =  FlowManager.getModelAdapter(Session.class);
                Session[] sessions = new Session[identifies.size()];

                int index = 0;
                for (Session.Identify identify : identifies) {
                    Session session = SessionHelper.findFromLocal(identify.id);
                    //如果本地不存在该会话
                    if (session==null){
                        //第一次聊天，创建一次新的会话
                        session = new Session(identify);
                    }
                    //刷新会话到Message当前最新状态
                    session.refreshToNow();
                    //会话保存在本地
                    adapter.save(session);
                    //添加到集合
                    sessions[index++] = session;
                }
                //调用进行一次通知分发
                instance.notifySave(Session.class,sessions);

            }
        }).build().execute();
    }

    /**
     * 通知监听器
     * @param <Data> 数据泛型
     */
    @SuppressWarnings("unchecked")
    public interface ChangedListener<Data extends BaseModel>{
        void onDataSave(Data... list);

        void onDataDelete(Data... list);
    }
}
